<!-- 主体部分，因为涉及到主体有两种展现形式，所以进行封装抽离 -->
<template>
  <a-spin :spinning="getTreeLoading || getListLoading || getCollectTreeLoading || getCollectListLoading">
    <div class="main-box">
      <!-- 左侧选择 -->
      <div class="main-box__item main-box__item--left">
        <div class="item__header">
          <!-- 拥有收藏的头部 -->
          <template v-if="hasCollect">
            <div :class="['header-type', currentType === 1 && 'active']" @click="toggleType(1)">我的{{ typeName }}</div>
            <div :class="['header-type', currentType === 2 && 'active']" @click="toggleType(2)">我的收藏</div>
          </template>
          <!-- 无收藏头部 -->
          <template v-else>
            <div :class="['header-type']">{{ typeName }}</div>
          </template>
        </div>

        <div class="item__body">
          <!-- 搜索栏 -->
          <div class="body__search">
            <a-form-item>
              <a-input v-model:value="searchValue" v-trim placeholder="请输入关键词" :disabled="disabled" @keyup.enter="onSearch">
                <template #prefix> <i class="iconfont icon-search-line" @click="onSearch"></i> </template>
              </a-input>
            </a-form-item>
          </div>

          <div class="mt-8 w-326 tree-body my-scrollbar">
            <!-- 搜索结果列表 -->
            <recycle-scroller
              v-show="isOpenSearch"
              class="select-item-list my-scrollbar"
              :items="searchList"
              :item-size="30"
              key-field="key"
              v-slot="{ item }"
            >
              <div class="pb-8">
                <div class="select-item">
                  <a-checkbox v-model:checked="item.checked" @change="e => changeCheckBox(item, e)">
                    <div class="select-item__name" :title="item.title">
                      <i :class="['iconfont', item.isLeaf ? nodeIcon.file : nodeIcon.folder]"></i>
                      {{ item.title }}
                    </div>
                  </a-checkbox>
                </div>
              </div>
            </recycle-scroller>

            <!-- 树形数据 -->
            <a-breadcrumb
              v-show="crumbs.length > 1 && !isOpenSearch"
              ref="breadcrumbRef"
              class="crumbs"
              :class="[crumbs.length > 1 && 'exceed']"
            >
              <template #separator><span>></span></template>
              <template v-for="(crumb, index) in crumbs" :key="index">
                <a-breadcrumb-item v-if="crumb.overlay === undefined" style="cursor: pointer" @click="breadClick(crumb)">
                  <span :title="crumb.name" class="bread-text elliptical-1">{{ crumb.name }}</span>
                </a-breadcrumb-item>

                <a-breadcrumb-item v-else>
                  {{ crumb.name }}
                  <template #overlay>
                    <a-menu>
                      <a-menu-item v-for="(it, index) in crumb.overlay" :key="index" @click="breadClick(it)">
                        <span :title="it.name">{{ it.name }} </span>
                      </a-menu-item>
                    </a-menu>
                  </template>
                </a-breadcrumb-item>
              </template>
            </a-breadcrumb>

            <recycle-scroller
              v-show="!isOpenSearch"
              :class="[!isOpenSearch ? 'select-item-list' : 'select-tree-item-list', 'my-scrollbar']"
              :items="treeData"
              :item-size="30"
              key-field="key"
              v-slot="{ item }"
              :style="{ height: 300 - breadcrumbRef?.$el.offsetHeight + 'px' }"
            >
              <div class="pb-8">
                <div class="select-item">
                  <a-checkbox
                    v-model:checked="item.checked"
                    :disabled="disabled || isDataDisable(item)"
                    @change="e => changeCheckBox(item, e)"
                  >
                    <div :class="['select-item__name', disabled ? 'disabled-color' : '']" :title="item.title">
                      <i :class="['iconfont', item.isLeaf ? nodeIcon.file : nodeIcon.folder, disabled ? 'disabled-color' : '']"></i>
                      {{ item.title }}
                    </div>
                  </a-checkbox>
                  <a-button v-if="!item.isLeaf" type="link" style="margin-right: 8px" @click.stop="nextLevel(item)">下级</a-button>
                </div>
              </div>
            </recycle-scroller>
          </div>
        </div>
      </div>

      <!-- 右侧结果 -->
      <div class="main-box__item main-box__item--right">
        <div class="item__header">
          <div class="header-title text-color-1">已选</div>

          <slot name="selected-tools"></slot>
          <div class="ml-16 text-color-2 font-size-14">
            <slot name="resultTotal" :folder-num="checkedFolderLength" :file-num="checkedItems.length - checkedFolderLength">
              <template v-if="selectDataType === 'folder'"> {{ checkedFolderLength }}个文件夹 </template>
              <template v-else-if="selectDataType === 'file'"> {{ checkedItems.length - checkedFolderLength }}个文件 </template>
              <template v-else-if="selectDataType === 'all'">
                {{ checkedFolderLength }}个文件夹，{{ checkedItems.length - checkedFolderLength }}个文件
              </template>
            </slot>
          </div>
          <div class="flex-1"></div>
          <a-button v-if="!disabled" type="link" @click="clearKeys">清空</a-button>
        </div>
        <div class="item__body my-scrollbar">
          <div class="select-item-list">
            <div v-for="item in checkedItems" :key="item.key" class="select-item">
              <div :title="item.title" class="select-item__name">
                <i :class="['iconfont', item.isLeaf ? nodeIcon.file : nodeIcon.folder]"></i>
                {{ item.title }}
              </div>
              <i v-if="!disabled" class="cursor-pointer text-color-4 icon-DeleteOutlined iconfont" @click="deleteKey(item.key)"></i>
            </div>
          </div>
        </div>
      </div>
    </div>
  </a-spin>
</template>

<script lang="ts" setup>
  import { cloneDeep } from 'g6-fn'
  import { computed, ref, watch } from 'vue'
  import { useRequest } from 'vue-request'

  import { defaultProps } from '../options'
  import { Props, TreeNode } from '../types/index'
  type CrumbType = {
    key: string
    name: string
    overlay?: CrumbType[]
  }
  const props = withDefaults(defineProps<Props>(), defaultProps)

  const emits = defineEmits<{
    (e: 'change', checkedIds: string[]): void
    (e: 'update:checkedIds', checkedIds: string[]): void
  }>()

  // 面包屑
  const crumbs = ref<CrumbType[]>([{ key: '', name: props.crumbFirstName }])
  // 面包屑的缓存
  let crumbsCopy = ref<CrumbType[]>([{ key: '', name: props.crumbFirstName }])
  // 隐藏的临界值
  const CRITICAL_VALUE = 4
  // 面包屑的ref
  const breadcrumbRef = ref()
  // 点击
  const breadClick = async (crumb: CrumbType) => {
    if (crumb.key === '') {
      clearBread()
    }
    await getTreeData(crumb.key)
    const index = crumbsCopy.value.findIndex(item => item.key === crumb.key) // 获取元素 a=3 的索引
    if (index !== -1) {
      crumbsCopy.value.splice(index + 1) // 截断数组
      if (crumbsCopy.value.length > CRITICAL_VALUE) {
        let arr = cloneDeep(crumbsCopy.value)
        const delList = arr.splice(2, arr.length - CRITICAL_VALUE)
        crumbs.value = arr
        crumbs.value.splice(2, 0, { name: '...', key: 'overlay', overlay: delList })
      } else {
        crumbs.value = cloneDeep(crumbsCopy.value)
      }
    }
  }

  /* 类型切换--------------------------------------------------- */
  const currentType = ref<1 | 2>(1) //1公开数据 2我的收藏

  const toggleType = (type: typeof currentType.value) => {
    currentType.value = type
    searchValue.value = ''
    isOpenSearch.value = false
    clearBread()
    getTreeData()
  }

  // 清除
  const clearBread = () => {
    // 面包屑
    crumbs.value = [{ key: '', name: props.crumbFirstName }]
    // 面包屑的缓存
    crumbsCopy.value = [{ key: '', name: props.crumbFirstName }]
  }

  /* 数据变动逻辑--------------------------------------------------- */
  const checkedKeys = ref<string[]>([]) // 选择的所有id
  const checkedItems = ref<TreeNode[]>([]) // 选择的所有数据
  const checkedFolderLength = computed(() => checkedItems.value.filter(it => !it.isLeaf).length)
  console.log('checkedItems:', checkedItems)

  watch(
    checkedKeys,
    () => {
      emits('change', checkedKeys.value)
    },
    {
      deep: true
    }
  )

  const isDataDisable = (item: TreeNode) => {
    switch (props.selectDataType) {
      case 'folder':
        return item.isLeaf

      case 'file':
        return !item.isLeaf

      default:
        return false
    }
  }

  const addKey = (item: TreeNode) => {
    if (checkedKeys.value.includes(item.key)) return
    checkedKeys.value.push(item.key)
    checkedItems.value.push(item)
    for (let it of treeData.value) {
      if (it.key === item.key) {
        it.checked = true
      }
    }
  }

  const deleteKey = (key: string) => {
    const index = checkedKeys.value.findIndex(k => k === key)

    if (index > -1) {
      checkedKeys.value.splice(index, 1)
      checkedItems.value.splice(index, 1)
      searchList.value.some(it => {
        if (it.key === key) {
          it.checked = false
          return true
        }
      })
      treeData.value.some(it => {
        if (it.key === key) {
          it.checked = false
          return true
        }
      })
    }
  }
  const clearKeys = () => {
    checkedKeys.value = []
    checkedItems.value = []
    searchList.value.forEach(it => (it.checked = false))
    treeData.value.forEach(it => (it.checked = false))
  }

  // 下一级
  const nextLevel = async (node: TreeNode) => {
    crumbsCopy.value.push({
      key: node.key,
      name: node.title
    })
    if (crumbsCopy.value.length > CRITICAL_VALUE) {
      let arr = cloneDeep(crumbsCopy.value)
      const delList = arr.splice(2, arr.length - CRITICAL_VALUE)
      crumbs.value = arr
      crumbs.value.splice(2, 0, { name: '...', key: 'overlay', overlay: delList })
    } else {
      crumbs.value.push({
        key: node.key,
        name: node.title
      })
    }

    await getTreeData(node.key)
  }

  const treeData = ref<TreeNode[]>([])
  const { loading: getTreeLoading, runAsync: runGetTreeData } = useRequest<ResponseData<any[]>>(props.getTreeDataApi)
  const { loading: getCollectTreeLoading, runAsync: runGetCollectTreeData } = useRequest<ResponseData<any[]>>(props.getCollectTreeDataApi)

  // 将数据渲染为树形节点
  const dataToTreeNode = (list: Record<string, any>[]): TreeNode[] => {
    return list.map((it: any) => ({
      title: it[props.fieldNames.title],
      key: it[props.fieldNames.key],
      children: it[props.fieldNames.children],
      isLeaf: it.type === 1, //0:文件夹 1:文件
      checked: checkedKeys.value.includes(it[props.fieldNames.key])
    }))
  }

  const getTreeData = async (id = '') => {
    const res = await (currentType.value === 1 ? runGetTreeData({ id }) : runGetCollectTreeData({ id }))
    treeData.value = dataToTreeNode(res.data)
  }

  /* 搜索模式逻辑--------------------------------------------------- */
  const isOpenSearch = ref<boolean>(false)
  const searchValue = ref('')
  const searchList = ref<TreeNode[]>([])

  const { loading: getListLoading, runAsync: runGetListData } = useRequest<ResponseData<any>>(props.getListDataApi, {
    onSuccess: res => {
      searchList.value = dataToTreeNode(res.data)
    }
  })
  const { loading: getCollectListLoading, runAsync: runGetCollectListData } = useRequest<ResponseData<any>>(props.getCollectListDataApi, {
    onSuccess: res => {
      searchList.value = dataToTreeNode(res.data)
    }
  })

  const onSearch = async () => {
    isOpenSearch.value = !!searchValue.value
    if (searchValue.value === '') {
      return
    }
    currentType.value === 1 ? runGetListData({ name: searchValue.value }) : runGetCollectListData({ name: searchValue.value })
  }

  const changeCheckBox = (item: TreeNode, e: any) => {
    if (!e.target.checked) {
      deleteKey(item.key)
      return
    }
    addKey(item)
  }

  /* -------------------------------------------------------- */

  defineExpose({
    getTreeData,
    checkedKeys,
    treeData,
    checkedItems,
    addKey
  })
</script>

<style lang="less" scoped>
  @import '@/styles/mixins.less';

  .main-box {
    display: flex;
    overflow: hidden;
    width: 704px;
    height: 400px;
    border: 1px solid @border2;
    border-radius: 6px;

    .main-box__item {
      width: 50%;
      height: 100%;

      .ant-form-item {
        margin-bottom: 0;
      }

      .item__header {
        display: flex;
        padding: 12px 14px 0;
        height: 44px;
        background: #fafafa;
        border-bottom: 1px solid @border1;

        .header-title {
          .font14b();
        }

        .header-type {
          .font14();

          cursor: pointer;
          display: flex;
          flex-direction: column;
          justify-content: space-between;

          &::after {
            content: '';
            display: block;
            width: 100%;
            height: 0;
            background: @primary1;
          }

          &.active {
            .font14b();

            &::after {
              height: 2px;
            }
          }

          & + .header-type {
            margin-left: 20px;
          }
        }
      }

      .item__body {
        overflow-y: auto;
        padding: 8px 12px;
        height: calc(398px - 36px);

        .tree-body {
          overflow: auto;
          height: 300px;

          :deep(.ant-tree-title) {
            display: block;
            width: 250px;
            .elliptical-1();
          }
        }

        .select-item-list {
          height: 100%;

          .select-item {
            display: flex;
            justify-content: space-between;
            color: @text01;

            .select-item__name {
              width: 250px;
              .elliptical-1();

              color: @text01;
            }

            & + .select-item {
              margin-top: 10px;
            }
          }
        }

        .select-tree-item-list {
          overflow: auto;
          height: 92%;

          .select-item {
            display: flex;
            justify-content: space-between;
            color: @text01;

            .select-item__name {
              width: 250px;
              .elliptical-1();

              color: @text01;
            }

            & + .select-item {
              margin-top: 10px;
            }
          }
        }
      }
    }

    .main-box__item--left {
      border-right: 1px solid @border2;
    }

    .main-box__item--right {
      .item__header {
        display: flex;
        padding: 12px 14px 0;
        height: 44px;
        background: #fafafa;
        border-bottom: 1px solid @border1;
      }

      .item__body {
        height: calc(380px - 36px);
      }
    }
  }

  .tree-node__title {
    .elliptical-1();
  }

  .exceed {
    padding-bottom: 8px;
    width: 300px;
  }

  .disabled-color {
    color: rgba(0, 0, 0, 0.25) !important;
  }

  .bread-text {
    display: table-cell;
    max-width: 200px;
  }

  :deep(.ant-breadcrumb-separator) {
    margin-inline: 6px;
  }
</style>
